package com.novel.framework.shiro.jwt.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.novel.framework.shiro.config.JwtProperties;
import com.novel.framework.shiro.utils.MD5Utils;
import com.novel.system.domain.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Date;

/**
 * jwt认证工具类
 *
 * @author novel
 * @date 2019/3/18
 */
@Component
public class JWTUtil {

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private static JWTUtil jwtUtil;

    // 过期时间30天
    // private static final long EXPIRE_TIME = 24 * 60 * 30 * 1000;
    private static final String CLAIM_NAME = "username";
    private static final String REFRESH_TOKEN_EXPIRE_TIME = "refreshTokenExpireTime";
    private static final String TOKEN_EXPIRE_TIME = "tokenExpireTime";
    private static final String SESSION_ID = "sessionId";

    @PostConstruct
    public void init() {
        jwtUtil = this;
        jwtUtil.jwtProperties = this.jwtProperties;
    }

    /**
     * 校验token是否正确
     *
     * @param token token签名
     * @param user  用户
     * @return
     */
    public static boolean verify(String token, SysUser user) {
        String password = MD5Utils.encryptPassword(user.getUserName(), user.getPassword(), user.getSalt());
        Algorithm algorithm = Algorithm.HMAC256(password);
        JWTVerifier verifier = JWT.require(algorithm)
                .withSubject(user.getUserName())
//                .withClaim(CLAIM_NAME, user.getUserName())
                .build();
        verifier.verify(token);
        return true;
    }

    /**
     * 获取token可刷新时间
     *
     * @param token token
     * @return 可刷新时间
     */
    public static Long getTokenExpireTime(String token) {
        Claim claim = getClaim(token, TOKEN_EXPIRE_TIME);
        return claim == null ? null : claim.asLong();
    }

    /**
     * 获取token刷新时间
     *
     * @param token token
     * @return 刷新时间
     */
    public static Date expiresAt(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getExpiresAt();
    }

    /**
     * 获取会话id
     *
     * @param token token
     * @return 会话id
     */
    public static String getSessionId(String token) {
        Claim claim = getClaim(token, SESSION_ID);
        return claim == null ? null : claim.asString();
    }

    /**
     * 获取id
     *
     * @param token token签名
     * @return id
     */
    public static String getId(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getId();
    }

    /**
     * 获取登录名
     *
     * @param token token签名
     * @return username登录名
     */
    public static String getUserName(String token) {
        Claim claim = getClaim(token, CLAIM_NAME);
        return claim == null ? null : claim.asString();
    }

    /**
     * 获取主题
     *
     * @param token token签名
     * @return 主题
     */
    public static String getSubject(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getSubject();
    }

    /**
     * 获取token签发时间
     *
     * @param token token签名
     * @return 签发时间
     */
    public static Date getIssuedAt(String token) {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getIssuedAt();
    }

    /**
     * 获取认证信息
     *
     * @param token token
     * @param claim claim
     * @return 认证信息
     */
    private static Claim getClaim(String token, String claim) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim(claim);
        } catch (JWTDecodeException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 生成签名
     *
     * @param user 用户
     * @return token 签名
     */
    public static String sign(SysUser user, String sessionId) {
        long currentTimeMillis = System.currentTimeMillis();

        // 指定过期时间，过期时间可以重新刷新token
        Date date = new Date(currentTimeMillis + jwtUtil.jwtProperties.getRefreshTokenExpireTime() * 60 * 1000);
        // token失效时间，失效后必须重新登录
        Long tokenExpireTime;
        if (user.isRememberMe()) {
            tokenExpireTime = jwtUtil.jwtProperties.getRememberMeTokenExpireTime();
        } else {
            tokenExpireTime = jwtUtil.jwtProperties.getTokenExpireTime();
        }

        String password = MD5Utils.encryptPassword(user.getUserName(), user.getPassword(), user.getSalt());
        Algorithm algorithm = Algorithm.HMAC256(password);
        return JWT.create()
//                .withClaim(CLAIM_NAME, user.getUserName())
//                .withClaim(SESSION_ID, sessionId)
//                .withClaim(REFRESH_TOKEN_EXPIRE_TIME, currentTimeMillis)
                .withClaim(TOKEN_EXPIRE_TIME, tokenExpireTime)
                .withIssuedAt(new Date(currentTimeMillis))//签发时间
                .withIssuer("sys")//签发者
                .withJWTId(sessionId)//jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击。
                .withSubject(user.getUserName())// jwt所面向的用户
                .withExpiresAt(date)//token 超时时间
                .withAudience("")//颁发给谁
                .withNotBefore(new Date(currentTimeMillis))//在此时间前不可用
                .sign(algorithm);
    }
}
